/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */

package main.NER;

import java.io.Serializable;
import java.util.*;
import main.KBProcess.Fragment;
import main.Ontology.ElementOntology;
import main.Ontology.ElementOntologySet;

/**
 *
 * @author Isaac Osesina
 */
public class DefaultBranch extends DefaultFruit implements Branch, Serializable, Cloneable  {

	private static final long serialVersionUID = -212542084342505939L;

	private ElementOntologySet branchID;

    private Branch parentBranch;

    private Map<ElementOntology, Branch> children;

    public DefaultBranch() {
    	this.children = new HashMap<ElementOntology, Branch>();
    }

    public DefaultBranch(ElementOntologySet branchID) {
    	this();
        this.branchID = branchID;
    }

    @Override
	public ElementOntologySet getBranchID() {
        return branchID;
    }

    @Override
	public Branch addChild(ElementOntologySet branchID, boolean append, 
                                    Integer totalFreq, Fragment fragment) {
        Branch child = null;
        if(branchID == null)
            return child;
        
        if(branchID.isEmpty()) {
            if(totalFreq == null)
                return child;
            
            if(children!=null) {
                for(Iterator<ElementOntology> iter = children.keySet().iterator(); iter.hasNext();) {
                    ElementOntology key = iter.next();
                    Branch child_1 = children.get(key);
//                    String name = key.getName();
                    child_1.setNonCousinTypeCount(totalFreq);
                    child_1.addChild(branchID, append, totalFreq, fragment);
                }
            }
            return child;
        }
        else {
            ElementOntology nextBranch = branchID.getHead();
            if(nextBranch != null) {
                ElementOntologySet nextBranchSet = new ElementOntologySet(branchID.getCache());
                nextBranchSet.add(nextBranch);
                child = addChild(nextBranchSet);
                child.incrementMyType(totalFreq);
                child.setAppend(append);
                child.setFragmentSample(fragment);
                if(totalFreq != null && !append) {
                    updateSisters(child);
                }
                ElementOntologySet tail = branchID.getTail();
                if(!tail.isEmpty()) {
                    child.addChild(tail, append, totalFreq, fragment);
                }
            }
        }        
        return child;
    }

    @Override
	public Branch addChild(ElementOntologySet branchID) {
        ElementOntology thisBranch = branchID.getHead();
        ElementOntologySet thisBranchSet = new ElementOntologySet(branchID.getCache());
        ElementOntologySet tail = branchID.getTail();
        Branch child = children.get(thisBranch);
        if(child==null) {
            child = new DefaultBranch(thisBranchSet);
            children.put(thisBranch, child);
//            child.setAppend(this.isAppend());
//            child.setTerminator(this.isTerminator());
            child.setParent(this);
            child.setCousinTypeList(this.getCousinTypeList());
        }
        if (!tail.isEmpty()) {
            child.addChild(tail);
        }
        return child;
    }

    /**
     * Edit the Fruit on this <code>Branch</code>
     * 
     * @param totalFreq should be null if
     * @return
     */

    //to be deleted
    @Override
	public Branch updateFruit_Terminator(ElementOntologySet branchID, Fragment fragment) {
        Branch child = null;
        if(branchID!=null && children != null) {
            ElementOntology thisBranch = branchID.getHead();
            ElementOntologySet tail = branchID.getTail();
            child = children.get(thisBranch);
            if(child != null) {
                child.setTerminator(true);
                child.addTerminatingFragment(fragment);
                Branch childByID = child.updateFruit_Terminator(tail, fragment);
                return childByID;
            }
        }
        return child;
    }


    /**
     * Update for other types different from given <code>Branch</code>.
     * Other children branches creates ambiguity
     * @param branch
     */
    @Override
	public void updateSisters(Branch branch) {
        if(children != null && branch != null) {
            for(Branch sister:children.values()) {
                if(sister != branch) {
                    sister.addCousinType(branch.getBranchID());
                    //update children of this branch
                    sister.updateChildren(branch);
                }
            }
        }
    }


    /**
     * Update children of current <code>Branch</code>.
     * This indicates that *context* is ambiguous for branch type
     * This ambiguity is caused by the parameter branch
     * @param branch branch that causes ambiguity for this branch's children
     */
    @Override
	public void updateChildren(Branch branch) {
        if(children != null) {
            for(Branch child:children.values()) {
                child.addCousinType(branch.getBranchID());
                child.updateChildren(branch);
            }
        }
    }


    @Override
	public Branch childByID(ElementOntologySet branchID) {
        Branch child = null;
        if(children != null) {
            ElementOntology thisBranch = branchID.getHead();
            ElementOntologySet tail = branchID.getTail();
            child = children.get(thisBranch);
            if(!tail.isEmpty() && child != null) {
                return child.childByID(tail);
            }
        }
        return child;
    }

    @Override
	public int childCount() {
        if(children != null) {
            return children.size();
        }
        return 0;
    }

    @Override
    public int countDescendants() {
        int result = 0;
        if(children != null)
        for (Branch child : children.values()) {
            result += 1 + child.countDescendants();
        }
        return result;
    }

    @Override
	public Iterator<Branch> branchIterator() {
    	return children.values().iterator();
    }

    @Override
	public Branch remove(Branch branch) {
        if(branch != null) {
            ElementOntologySet branchID1 = branch.getBranchID();
            if(branchID1 != null) {
                return children.remove(branchID1);
            }
        }
        return branch;
    }

    @Override
	public Branch remove(ElementOntologySet branchID) {
        if(branchID != null) {
            return children.remove(branchID);
        }
        return null;
    }


    @Override
	public boolean hasChildren() {
        return childCount()>0 ? true:false;
    }

    @Override
	public boolean isLeaf() {
        return isTerminator();
    }

//    public Set<Object> addCousinType(Object o) {
//        throw new UnsupportedOperationException("Not supported yet.");
//    }

    @Override
	public List<Branch> getChildren() {
        if(children != null) {
            return new ArrayList<Branch> (children.values());
        }
        return null;
    }

    @Override
	public Branch setParent(Branch parent) {
        return this.parentBranch = parent;
    }

    @Override
	public Branch getParent() {
        return parentBranch;
    }

    @Override
	public int depth() {
		int depth = 0;

		for (Branch child : children.values()) {
			depth++;
			int depth_child = depth + child.depth();
			if (depth_child > depth) {
				depth = depth_child;
			}
		}
		return depth;
	}

    @Override
    public List<ElementOntologySet> youngestDescendant() {
        List<ElementOntologySet> depth = new ArrayList<ElementOntologySet>();
		for (Branch child : children.values()) {
			depth.add(child.getBranchID());
			List<ElementOntologySet> depth_child = child.youngestDescendant();
			depth_child.addAll(depth);
			if (depth_child.size() > depth.size()) {
				depth = depth_child;
			}
		}
        return depth;
    }

    @Override
	public List<Branch> youngestBranches() {
        List<Branch> depth = new ArrayList<Branch>();
        if(children.isEmpty()) {
            depth.add(this);
            return depth;
        }
        else {
            for (Branch child : children.values()) {
                List<Branch> depth_child = child.youngestBranches();
                depth.addAll(depth_child);
            }
        }
        return depth;
    }

    @Override
	public List<Branch> getLeafs() {
        List<Branch> leafs = new ArrayList<Branch>();
        if(children.isEmpty()) {
            leafs.add(this);
        }
        else {
            for(Branch child:children.values()) {
                child.getLeafs();
            }
        }
        return leafs;
    }

    @Override
	public List<Branch> getBranchesWithTerminatorFruit() {
        List<Branch> branches = new ArrayList<Branch>();
        if(this.isTerminator()) {
            branches.add(this);
        }
        for(Branch child:children.values()) {
            child.getLeafs();
        }
        return branches;
    }

    @Override
	public Set<Branch> getAmbiguousLeafBranches() {
        Set<Branch> result = new LinkedHashSet<Branch>();
        if(this.isAmbiguous()) {
        	if(!children.isEmpty()) {
                for(Branch child:children.values()) {                    
                    result.addAll(child.getAmbiguousLeafBranches());
                }
        	}
            else {
                if(isTerminator() && isAmbiguous()) {
//                    if(!isAppend())
                        result.add(this);
                }
            }
        }
        return result;
    }

    @Override
    public boolean isAmbiguous() {
        if(children.isEmpty()) {
            return super.isAmbiguous();
        }
        if(children.size() > 1) {
            return true;
        }
        for(Branch child:children.values()) {
            return child.isAmbiguous();
        }
        return true;
    }
}
